<?php

class Template
{

	/**
	 * @var Template Object
	 */
	static protected $_inst;

	/**
	 * @return Template Object
	 */
	static public function inst($name = null)
	{
		return (null == self::$_inst) ? new self : self::$_inst;
	}

	/**
	 * execute request.
	 * @return string
	 */
	public function execute($request)
	{

		Agilet::emit('before_page_render');
		/**
		 * process structure to make sure what happend there.
		 */

		$data = array('template' => $this);

		$master = 'default';

		$template = 'home';

		foreach (array('header','content','footer') as $section)
		{
			$this -> _layout = new Template_Layout($master, $section);
			$data[$section] = $this -> _layout -> compile();
		}

		$script = AGILET_TEMPLATE_PATH . '/master/default.tpl';

		$html = View::render($data, $script, 0);

		Agilet::emit('afterPageRender');

		return $html;
	}

	/**
	 * @var string
	 */
	protected $_name = null;

	/**
	 * @var array
	 */
	protected $_structure = null;

	/**
	 * @var array
	 */
	protected $_headVars = array();

	/**
	 * dead constructor
	 * labosact
	 */
	protected function __construct()
	{
		$this -> _config = Agilet::loadConfig('info.php');
		self::$_inst = $this;
	}

	/**
	 * @param string $name
	 * @param mixed $default
	 * @return mixed
	 */
	public function getConfig($key, $default = null)
	{
		return isset($this -> _config[$key]) ? $this -> _config[$key] : $default;
	}

	/**
	 * @param array $options
	 * @return
	 */
	public function setConfig($key, $value)
	{
		$this -> _config[$key] = $value;
		return $this;
	}

	/**
	 * @param string $name
	 * @return array
	 */
	public function getHeadVar($key)
	{
		return isset($this -> _headVars[$key]) ? $this -> _headVars[$key] : array();
	}

	/**
	 * @return string
	 */
	public function renderHead()
	{

		$theme = 'admin';

		$staticUrl = Agilet::getStaticUrl();

		$appUrl = Agilet::getAppUrl();

		/**
		 * process some thing else as content or not of page.
		 */
		$this -> addMeta(array(
			'http-equiv' => 'content-type',
			'content' => 'text/html; charset=utf-8'
		)) -> addMeta(array(
			'http-equiv' => 'content-language',
			'content' => $this -> getConfig('content-language', 'en-us')
		)) -> addMeta(array(
			'name' => 'owner',
			'content' => $this -> getConfig('owner')
		)) -> addMeta(array(
			'name' => 'author',
			'content' => $this -> getConfig('author')
		)) -> addMeta(array(
			'name' => 'generator',
			'content' => $this -> getConfig('generator')
		)) -> addLink(array(
			'rel' => 'shortcut icon',
			'type' => 'image/x-icon',
			'href' => $staticUrl . '/favicon.ico'
		));

		$this -> prependCssLink($staticUrl . 'static/minify/index.php?g=require.css&b=nuo2');
		$this -> prependCssLink($staticUrl . 'static/' . $theme . '/styles/main.css');
		$this -> prependJsLink($staticUrl . 'static/minify/index.php?g=require.js&b=nuo2&lang=vi');
		$this -> prependJsLink($staticUrl . 'static/minify/index.php?g=main.js&b=nuo2');
		;

		/**
		 * @var array
		 */
		$allows = array(
			'Base',
			'Link',
			'CssLink',
			'Meta',
			'Keyword',
			'Description',
			'Title',
			'Script',
			'Css',
			'JsLink',
			'Script'
		);

		/**
		 * @var array
		 */
		$xhtml = array();

		foreach ($allows as $allow)
		{
			if (($html = $this -> {'_renderHead' . $allow}()) != '')
			{
				$xhtml[] = $html;
			}
		}
		return implode(PHP_EOL, $xhtml);
	}

	public function addCssLink($value)
	{
		return $this -> __processHead('add', 'CssLink', $value);
	}

	public function appendCssLink($value)
	{
		return $this -> __processHead('append', 'CssLink', $value);
	}

	public function prependCssLink($value)
	{
		return $this -> __processHead('prepend', 'CssLink', $value);
	}

	public function addJsLink($value)
	{
		return $this -> __processHead('add', 'JsLink', $value);
	}

	public function appendJsLink($value)
	{
		return $this -> __processHead('append', 'JsLink', $value);
	}

	public function prependJsLink($value)
	{
		return $this -> __processHead('prepend', 'JsLink', $value);
	}

	public function addLink($value)
	{
		return $this -> __processHead('add', 'Link', $value);
	}

	public function appendLink($value)
	{
		return $this -> __processHead('append', 'Link', $value);
	}

	public function prependLink($value)
	{
		return $this -> __processHead('prepend', 'Link', $value);
	}

	public function addScript($value)
	{
		return $this -> __processHead('add', 'Script', $value);
	}

	public function appendScript($value)
	{
		return $this -> __processHead('append', 'Script', $value);
	}

	public function prependScript($value)
	{
		return $this -> __processHead('prepend', 'Script', $value);
	}

	public function addMeta($value)
	{
		$this -> _headVars['Meta'][] = $value;
		return $this;
	}

	public function appendMeta($value)
	{
		return $this -> __processHead('append', 'Meta', $value);
	}

	public function prependMeta($value)
	{
		return $this -> __processHead('prepend', 'Meta', $value);
	}

	public function addTitle($value)
	{
		return $this -> __processHead('add', 'Title', $value);
	}

	public function appendTitle($value)
	{
		return $this -> __processHead('append', 'Title', $value);
	}

	public function prependTitle($value)
	{
		return $this -> __processHead('prepend', 'Title', $value);
	}

	public function addKeyword($value)
	{
		return $this -> __processHead('add', 'Keyword', $value);
	}

	public function appendKeyword($value)
	{
		return $this -> __processHead('append', 'Keyword', $value);
	}

	public function prependKeyword($value)
	{
		return $this -> __processHead('prepend', 'Keyword', $value);
	}

	public function addDescription($value)
	{
		return $this -> __processHead('add', 'Description', $value);
	}

	public function appendDescription($value)
	{
		return $this -> __processHead('append', 'Description', $value);
	}

	public function prependDescription($value)
	{
		return $this -> __processHead('prepend', 'Description', $value);
	}

	/**
	 * placement
	 * @param string $name
	 * @param string/array $value
	 * @return mixed
	 */
	protected function __processHead($action, $key, $value = null)
	{

		if (!isset($this -> _headVars[$key]))
		{
			$this -> _headVars[$key] = array();
		}

		if (method_exists($this, $method = 'correct' . $key))
		{
			$value = $this -> {$method}($value);
		}

		switch($action) {
			case 'append' :
			case 'add' :
				$this -> _headVars[$key][] = $value;
				return $this;
			case 'prepend' :
				$this -> _headVars[$key] = array_reverse($this -> _headVars[$key]);
				$this -> _headVars[$key][] = $value;
				$this -> _headVars[$key] = array_reverse($this -> _headVars[$key]);
				return $this;
			case 'clear' :
				$this -> _headVars[$key] = array();
				return $this;
			case 'set' :
				$this -> _headVars[$key] = array($value);
			case 'has' :
				return isset($this -> _headVars[$key]) && !empty($this -> _headVars[$key]);
			case 'none' :
		}
		return $this;
	}

	/**
	 * match action
	 * @param string $name
	 * @param string $option
	 * @param string $default
	 */
	protected function __empty($name, $option, $default = '')
	{

		$value = $this -> getConfig($option, $default = '');

		if (isset($this -> _vars[$name]) && !empty($this -> _vars[$name]))
		{
			$this -> process($this -> getConfig($option . '-option', ' append'), $name, $value);
		}
		else
		{
			$this -> process('set', $name, $value);
		}
		return $this;
	}

	static protected function __flat($array)
	{
		$xhtml = array();
		if (is_array($array))
		{
			foreach ($array as $key => $value)
			{
				$xhtml[] = sprintf('%s="%s"', $key, $value);
			}
		}

		return implode(' ', $xhtml);
	}

	/**
	 * get html from current tile
	 * @return
	 */
	protected function _renderHeadTitle()
	{
		$vars = $this -> getHeadVar('Title');

		$sep = $this -> getConfig('title-separate', '-');

		if (empty($vars))
		{
			$vars = array($this -> getConfig('title'));
		}

		return sprintf('<title>%s</title>', implode($sep, $vars));
	}

	/**
	 * flatten meta to html
	 * @return string
	 */
	protected function _renderHeadMeta()
	{
		$xhtml = array();

		foreach ($this->getHeadVar('Meta') as $meta)
		{
			$xhtml[] = '<meta ' . $this -> __flat($meta) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten keyword to html
	 * @return string
	 */
	protected function _renderHeadKeyword()
	{
		$vars = $this -> getHeadVar('Keyword');
		if (empty($keyword))
		{
			$vars = array($this -> getConfig('keyword'));
		}
		return '<meta name="keyword" content="' . implode(', ', $vars) . '"/>';
	}

	/**
	 * flatten description to html
	 * @return string
	 */
	protected function _renderHeadDescription()
	{
		$vars = $this -> getHeadVar('Description');
		if (empty($keyword))
		{
			$vars = array($this -> getConfig('description'));
		}
		return '<meta name="description" content="' . implode(', ', $vars) . '"/>';
	}

	/**
	 * flatten link to html
	 * @return string
	 */
	protected function _renderHeadLink()
	{
		$xhtml = array();

		foreach ($this->getHeadVar('Link') as $value)
		{
			$xhtml[] = '<link ' . $this -> __flat($value) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten css link to html
	 * @return string
	 */
	protected function _renderHeadCssLink()
	{
		$xhtml = array();

		foreach ($this->getHeadVar('CssLink') as $value)
		{
			$xhtml[] = '<link ' . $this -> __flat($value) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten js link to html
	 * @return string
	 */
	protected function _renderHeadJsLink()
	{
		$xhtml = array();
		foreach ($this->getHeadVar('JsLink') as $value)
		{
			$xhtml[] = '<script ' . $this -> __flat($value) . '></script>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * get base href tags
	 * @return string
	 */
	protected function _renderHeadBase()
	{
		return '<base href="http://namnv.younetco.com/agis3/"/>';
	}

	/**
	 * flatten opengrap to meta, it is helpful for social sites to know what is
	 * this
	 * site
	 * @return string
	 */
	protected function _renderHeadOpengraph()
	{
		return '';
	}

	protected function _renderHeadScript()
	{
		$scripts = implode(';',$this -> getHeadVar('Script'));;
		if ($scripts)
		{
			return '<script type="text/javascript">'.$scripts.'</script>';
		}
		return '';
	}

	protected function _renderHeadCss()
	{

	}

	protected function _correctCssLink($value)
	{
		if (is_string($value))
		{
			$value = array('href' => $value);
		}
		return array_merge(array(
			'type' => 'text/css',
			'rel' => 'stylesheet',
			'media' => 'all'
		), $value);
	}

	protected function _correctJsLink($value)
	{
		if (is_string($value))
		{
			$value = array('src' => $value);
		}
		return array_merge(array('type' => 'text/javascript'), $value);
	}

}

class Template_Layout
{

	protected $_master = null;

	protected $_content = null;

	protected $_header = null;

	protected $_footer = null;

	protected $_data = null;

	public function __construct($master, $section, $layout, $default)
	{
		// try to get layout or does not exsits.
		$this -> _section = $section;

		$this -> _master = $master;

		// test cache
		$cache = null;

		$this -> _data = $this -> prepareData($layout);

	}

	public function prepareData($layout)
	{
		$data = array();

		// renew cache from elements.
		$items = DbNosql::inst() -> find('agilet_template_elements', array('layout_id' => $layout));

		foreach ($items as $item)
		{
			$data[] = $item;
		}

		/**
		 * build parent/child structure
		 */
		// $data = array('area' => array(
		// 'stage' => array( array('spec' => 'block/menu/header/mini')),
		// 'content' => array( array('spec' => 'block/content'))
		// ));

		return $data;
	}

	public function getLayoutFile()
	{

		$layout = isset($this -> _data['layout']) ? $this -> _data['layout'] : 'default';

		$filename = AGILET_TEMPLATE_PATH . "/{$this->_section}/$layout.tpl";

		if ($layout == 'default' || file_exists($filename))
		{
			return $filename;
		}

		return APP_PATH . "/template/{$this->_section}/default.tpl";
	}

	/**
	 * check if accept empty content.
	 */
	public function renderArea($name, $force_empty = false)
	{
		$html = '';

		if ($name == 'content')
		{
			$html = Request::initiator() -> response;
		}
		else
		{
			$area = isset($this -> _data['area'][$name]) ? $this -> _data['area'][$name] : NULL;

			if (!empty($area))
			{
				$html = $this -> renderElements($area, $name);
			}

			if ($force_empty == false && '' == $html)
			{
				return '';
			}
		}
		return sprintf('<div class="layout-area-%s">%s</div>', $name, $html);
	}

	/**
	 * $eles = array[array('id'=>int,
	 * 'action_id'=>'string','params'=>string,'children'=>array())]
	 * @param array $blocks
	 *
	 */
	public function renderElements($elements)
	{
		$xhtml = array();

		if (is_array($elements))
		{
			foreach ($elements as $element)
			{
				$xhtml[] = $this -> renderElement($element, null);
			}

		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * $ele = array('id'=>int,
	 * 'action_id'=>'string','params'=>string,'children'=>array())
	 * @param array $ele
	 * @param string $actionId
	 * @return string
	 */
	public function renderElement($element = null)
	{

		$params = array();
		$spec = $element['spec'];
		$html = Request::factory($spec, $params) -> execute() -> response;
		return sprintf("<div>%s</div>", $html);
	}

	public function compile()
	{
		$script = $this -> getLayoutFile();

		return View::render(array(
			'layout' => $this,
			'master' => $this -> _master,
			'section' => $this -> _section
		), $script, 0);
	}

}
